﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Xml.Serialization;

namespace DELVE_GAME
{
    enum TILE_VARIANTS
    {
        TILE,
        HIGH_BANDWIDTH,
        LOW_BANDWIDTH,
        WIFI,
        BROKEN_LINK,
        BUS,
        FIREWALL,
        DYING_LINK,
        NUM_TYPES
    }

    enum DIRECTIONS
    {
        LEFT,
        RIGHT,
        UP,
        DOWN,
        NUM_DIRS
    }

    [Serializable]
    [XmlInclude(typeof(HighBandwidthTile))]
    [XmlInclude(typeof(LowBandwidthTile))]
    [XmlInclude(typeof(WiFiTile))]
    [XmlInclude(typeof(BrokenLinkTile))]
    [XmlInclude(typeof(BusTile))]
    [XmlInclude(typeof(DyingLinkTile))]
    [XmlInclude(typeof(FirewallTile))]
    public class Tile
    {
        bool futureBool = false;
        public int type;
        public int orientation;
        protected Point location;
        protected List<Tile> adjacents;
        [NonSerialized] protected Virus virus;
        protected Dictionary<Virus, int> marks;

        //List<Virus>[] influence = new List<Virus>[4];
        //List<Tile> virtualExpand = new List<Tile>();

        public Tile(Tile currentTile) {
            futureBool = true;
            this.type = currentTile.type;
            this.orientation = currentTile.orientation;
            this.location = currentTile.location;

            this.adjacents = new List<Tile>();
            //this.adjacents = currentTile.adjacents;
            /*this.adjacents = new List<Tile>();
            foreach (Tile t in currentTile.adjacents) {
                this.adjacents.Add(new Tile(t));
            }*/

            this.virus = currentTile.virus;
            //this.virus = new Virus(currentTile.virus);

            this.marks = new Dictionary<Virus, int>();
            foreach (KeyValuePair<Virus, int> k in currentTile.marks) {
                this.marks.Add((Virus)(k.Key), k.Value);    
            }
        }

        public override string ToString()
        {
            if (virus == null)
                return " ";
            else
                return virus.getPlayerIndex().ToString();
        }

        #region get/set

        public Point getLocation() { return location; }
        public List<Tile> getAdjacents() {return adjacents; }

        public Virus getVirus() {return virus;}
        public bool isOccupied() {return (this.virus != null);}

        public virtual void setOrientation(int direction)
        {
            orientation = direction;
        }

        public void rotateTile()
        {
            int newOr = (orientation + 1) % (int) DIRECTIONS.NUM_DIRS;
            setOrientation(newOr);
        }

        public List<int> getVirusStats()
        {
            return (virus != null ? virus.getStats() : new List<int>());
        }

        //Returns adjacents and diagonal squares
        protected List<Tile> getOutbreakAdjacents()
        {
            HashSet<Tile> outbreakAdjs = new HashSet<Tile>(adjacents);
            HashSet<Tile> aggregator = new HashSet<Tile>();
            foreach (Tile t in outbreakAdjs)
            {
                foreach (Tile adjT in t.getAdjacents())
                {
                    Point diagLoc = adjT.getLocation();
                    if (diagLoc.X <= location.X + 1 &&
                        diagLoc.X >= location.X - 1 &&
                        diagLoc.Y <= location.Y + 1 &&
                        diagLoc.Y >= location.Y - 1)
                    {
                        aggregator.Add(adjT);
                    }
                }
            }
            outbreakAdjs.UnionWith(aggregator);

            List<Tile> myD = new List<Tile>(outbreakAdjs);
            return myD;
        }

        //Recursively determine visibility based on virus speed
        public HashSet<Tile> getVisibleTiles(int numIts, bool outbreak)
        {
            if (numIts == 0) return new HashSet<Tile>();
            HashSet<Tile> visible = new HashSet<Tile>();
            if (outbreak) visible.UnionWith(getOutbreakAdjacents());
            else visible.UnionWith(adjacents);

            HashSet<Tile> tempVisible = new HashSet<Tile>();
            foreach (Tile t in visible)
            {
                tempVisible.UnionWith(t.getVisibleTiles(numIts - 1, outbreak));
            }
            visible.Add(this);
            visible.UnionWith(tempVisible);
            return visible;
        }

        #endregion

        #region initializers

        public Tile()
        {
            this.type = (int)TILE_VARIANTS.TILE;
            this.location = new Point(0, 0);
            this.adjacents = new List<Tile>();
            marks = null;
        }

        public Tile(Point myLocation)
        {
            this.type = (int)TILE_VARIANTS.TILE;
            this.location = myLocation;
            this.adjacents = new List<Tile>();
            marks = new Dictionary<Virus, int>();


            //***********************************************************************Rick Alert Changes*************************************************************************
            /*influence[0] = new List<Virus>();
            influence[1] = new List<Virus>();
            influence[2] = new List<Virus>();
            influence[3] = new List<Virus>();*/
            //***********************************************************************Rick Alert Changes*************************************************************************

        }
        
        //save references to adjacent tiles
        public virtual void placeAdjacents(List<Tile> newAdjs)
        {
            this.adjacents.Clear();
            foreach (Tile t in newAdjs)
                this.adjacents.Add(t);
        }

        public virtual Virus placeBase(int playerIndex)
        {
            if (isOccupied()) return null;

            virus = new Virus(this, playerIndex);
            return virus;
        }

        //plant a virus on a tile
        public virtual void plantVirus(Virus v) {virus = v;}

        #endregion

        #region marks

        protected bool writeMark(Virus startVirus, int newPriority) {
            bool wrote = true;
            int priority;
            if (marks.TryGetValue(startVirus, out priority))
            {
                if (newPriority >= priority)
                {
                    marks[startVirus] = newPriority;
                }
                else wrote = false;
            }
            else marks.Add(startVirus, newPriority);
            return wrote;
        }
        public void removeMark(Virus startVirus)
        {
            if (marks.ContainsKey(startVirus))
                marks.Remove(startVirus);
        }
        public bool canFinalize(Virus start, int finalizePriority)
        {
            //Finalize if this path to the square is the best
            int priority;
            return (marks.TryGetValue(start, out priority) && finalizePriority >= priority);
        }
        public void clearMarks() {marks.Clear();}

        protected bool markOtherTile(Tile toMark, Virus startVirus, int priority)
        {
            //Tiles are marked if empty or inactive or occupied by an enemy.
            bool marked = false;
            if (!toMark.isOccupied() || !toMark.getVirus().isActive() || 
                !Virus.isFriend(startVirus, toMark.getVirus()))
            {
                marked = toMark.writeMark(startVirus, priority);
            }
            return marked;
        }

        #endregion

        #region expansion

        public List<Virus> expand()
        {
            int iters = virus.getSpeed() - (int)VIRUS_DEFAULTS.SPEED + 1;
            tryExpand(iters, this.virus);
            return finalizeExpansion(iters, this.virus);
        }

        public virtual void tryExpand(int iters, Virus startVirus)
        {
            if (iters == 0) return;

            List<Tile> expandAdjacents = (startVirus.hasOutbreak() ? getOutbreakAdjacents() : adjacents);

            foreach (Tile adj in expandAdjacents)
            {
                    //Mark expandable tile
                if (!markOtherTile(adj, startVirus, iters)) continue;

                    //Crawl through unoccupied tiles or inactive friendly ones
                    if (!adj.isOccupied() ||
                        (Virus.isFriend(startVirus.getPlayerIndex(), adj.getVirus()) && !adj.getVirus().isActive()))
                    {
                        adj.tryExpand(iters - 1, startVirus);
                    }
            }
        }

        public virtual List<Virus> finalizeExpansion(int iters, Virus startVirus)
        {
            if (iters == 0) return null;

            List<Tile> expandAdjacents = (startVirus.hasOutbreak() ? getOutbreakAdjacents() : adjacents);

            List<Virus> newViruses = new List<Virus>();
            List<Virus> tempViruses = new List<Virus>();

            //Investigate marks of every adjacent
            foreach (Tile adj in expandAdjacents)
            {
                if (!adj.canFinalize(startVirus, iters)) continue;

                bool addNewVirus = true;
                bool recurse = false;
                if (adj.isOccupied())
                {
                    bool isFriend = Virus.isFriend(virus.getPlayerIndex(), adj.getVirus());
                    if (!adj.getVirus().isActive())
                    {
                        addNewVirus = false;
                        if (!isFriend &&
                            (virus.getRawPotency() - (int)VIRUS_DEFAULTS.POTENCY_MOD ==
                             adj.getVirus().getRawPotency()))
                        {
                            adj.getVirus().kill();
                        }
                        else if (adj.getVirus().decayCopy(virus)) recurse = true;
                    }
                    else if (!isFriend) addNewVirus = !adj.defend(startVirus.getPlayerIndex());
                }
                if (addNewVirus)
                {
                    Virus newVirus = new Virus(this.virus, adj);
                    if (newVirus.isAlive())
                    {
                        newViruses.Add(newVirus);
                        recurse = true;
                    }
                    else newVirus.kill();
                }
                if (recurse)
                {
           //         adj.removeMark(startVirus);
                    tempViruses = adj.finalizeExpansion(iters - 1, startVirus);
                    if (tempViruses != null) newViruses.AddRange(tempViruses);
                }
            }

            return newViruses;
        }


        /*
         *returns whether attacker should expand 
         */
        public bool defend(int playerAttacking)
        {

            int myPotency = this.virus.getConflictPotency();
            int friendSupport = 0;
            int enemySupport = 0;
            int maxEnemy = 0;
            foreach (Tile adj in getOutbreakAdjacents())
            {
                Virus v = adj.getVirus();
                if (v == null || !(v.hasOutbreak() ?
                    adj.getOutbreakAdjacents().Contains(this) :
                    adj.getAdjacents().Contains(this))) continue;

                if (Virus.isFriend(virus, v))  
                {
                    friendSupport += v.getConflictPotency();
                }
                else if (Virus.isFriend(playerAttacking, v))
                {
                    int tempEnemy = v.getConflictPotency();
                    if (v.getPlayerIndex() == playerAttacking && tempEnemy > maxEnemy)
                    {
                        enemySupport += maxEnemy; //add what we THOUGHT was the max enemy
                        maxEnemy = tempEnemy;
                    }
                    else enemySupport += tempEnemy;
                }
            }

            double result = myPotency + friendSupport / 2.0 - (maxEnemy + enemySupport / 2.0);

            if (result <= 0) 
                this.virus.kill();
            return (result > 0);
        }

        public virtual bool age() {
            return true;
        }

        public virtual bool isAlive() { return true; }

        #endregion


        //***********************************************************************Rick Alert Changes*************************************************************************
       /* public virtual List<Tile> tryExpand(Virus virtualVirus)
        {
            int iters = virus.getSpeed() - (int)VIRUS_DEFAULTS.SPEED + 1;
            virtualExpand = new List<Tile>();
            tryExpand(iters, virus.getPlayerIndex(), virus.hasOutbreak(), virtualVirus);
            return virtualExpand;
        }

        public virtual void tryExpand(int iters, int player, bool outbreak, Virus virtualVirus)
        {
            if (iters == 0) return;
            List<Tile> expandAdjacents;
            if (outbreak) expandAdjacents = getOutbreakAdjacents();
            else expandAdjacents = adjacents;

            foreach (Tile adj in expandAdjacents)
            {
                virtualExpand.Add(adj);
                adj.influence[player].Add(virtualVirus);
                adj.tryExpand(iters - 1, player, outbreak, virtualVirus);
                /*
                //Mark expandable tile
                if (!markOtherTile(adj, player)) continue;

                //Crawl through unoccupied tiles or inactive friendly ones
                if (!adj.isOccupied() ||
                    (Virus.isFriend(player, adj.getVirus()) && !adj.getVirus().isActive()))
                {
                    adj.tryExpand(iters - 1, player, outbreak);
                }star/
            }
        }
        
        public virtual void removeInfluence(int myPlayer, Virus virtualVirus)
        {
            for (int i = 0; i < 4; i++)
            {
                //if (i != myPlayer)
                //{
                foreach (Virus v in influence[i])
                {
                    Console.Write(v.getPlayerIndex()+", ");
                }
                Console.WriteLine();
                    this.influence[i].Remove(virtualVirus);
                //}
                    Console.Write("After: ");
                    foreach (Virus v in influence[i])
                    {
                        Console.Write(v.getPlayerIndex() + ", ");
                    }
                    Console.WriteLine();
            }
        }

        /*public virtual bool surroundedByAlly(Virus v)
        {
            foreach (Tile t in adjacents)
            {
                Virus tv = t.getVirus();
                if (tv != null)
                {
                    if (tv.getPlayerIndex() != v.getPlayerIndex())
                        return false;
                }
            }
            return true;
        }/star/
        public virtual bool isInfluenced()
        {//if true make tile blink, else normal
            int i = 0;
            while (i < 4)
            {
                if (i != this.virus.getPlayerIndex())
                {
                    if (this.influence[i].Count != 0)
                        return true;
                }
                i++;
            }
            return false;
        }*/
        //***********************************************************************Rick Alert Changes*************************************************************************

        public override bool Equals(Object obj)
        {
            if (this != null & obj != null & this.location.Equals(((Tile)obj).location))
                return true;
            else
                return false;
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }//*/

    }

    #region subclasses

    [Serializable]
    public class HighBandwidthTile : Tile
    {

        public HighBandwidthTile()
            : base() { this.type = (int)TILE_VARIANTS.HIGH_BANDWIDTH; }

        public HighBandwidthTile(Point myLocation)
            : base(myLocation) { this.type = (int)TILE_VARIANTS.HIGH_BANDWIDTH; }

        public override void tryExpand(int iters, Virus startVirus)
        {
            tryExpand(iters, false, startVirus);
        }

        public void tryExpand(int iters, bool speedBoost, Virus startVirus)
        {
            if (iters == 0) return;

            List<Tile> expandAdjacents;
            if (startVirus.hasOutbreak()) expandAdjacents = getOutbreakAdjacents();
            else expandAdjacents = adjacents;

            foreach (Tile adj in expandAdjacents)
            {
                //Mark expandable tile
                if (!markOtherTile(adj, startVirus, iters)) continue;

                //Crawl through unoccupied tiles or inactive friendly ones
                if (!adj.isOccupied() ||
                    ((Virus.isFriend(startVirus, adj.getVirus())) && !adj.getVirus().isActive()))
                {
                    if (adj is HighBandwidthTile)
                    {
                        //if speedBoost = true, you're coming off a speed boost
                        int tempIt = iters;
                        if (speedBoost)
                        {
                            tempIt--;
                        }

                        ((HighBandwidthTile)adj).tryExpand(tempIt, !speedBoost, startVirus);
                    }
                    else adj.tryExpand(iters - 1, startVirus);
                }
            }
        }

        public override List<Virus> finalizeExpansion(int iters, Virus startVirus)
        {
            return finalizeExpansion(iters, false, startVirus);
        }

        public List<Virus> finalizeExpansion(int iters, bool speedBoost, Virus startVirus)
        {
            if (virus == null || iters == 0) return null;

            List<Tile> expandAdjacents = (startVirus.hasOutbreak() ? getOutbreakAdjacents() : adjacents);

            List<Virus> newViruses = new List<Virus>();
            List<Virus> tempViruses = new List<Virus>();

            //Investigate marks of every adjacent
            foreach (Tile adj in expandAdjacents)
            {
                if (!adj.canFinalize(startVirus, iters)) continue;

                bool addNewVirus = true;
                bool recurse = false;
                if (adj.isOccupied())
                {
                    bool isFriend = Virus.isFriend(virus.getPlayerIndex(), adj.getVirus());
                    if (!adj.getVirus().isActive())
                    {
                        addNewVirus = false;
                        if (!isFriend &&
                            (virus.getRawPotency() - (int)VIRUS_DEFAULTS.POTENCY_MOD ==
                             adj.getVirus().getRawPotency()))
                        {
                            adj.getVirus().kill();
                        }
                        else if (adj.getVirus().decayCopy(virus)) recurse = true;
                    }
                    else if (!isFriend) addNewVirus = !adj.defend(startVirus.getPlayerIndex());
                }
                if (addNewVirus)
                {
                    Virus newVirus = new Virus(this.virus, adj);
                    if (newVirus.isAlive())
                    {
                        newViruses.Add(newVirus);
                        recurse = true;
                    }
                    else newVirus.kill();
                }
                if (recurse)
                {
              //      adj.removeMark(startVirus);

                    if (adj is HighBandwidthTile)
                    {
                        int tempIt = iters;
                        //if speedBoost = true, you're coming off a speed boost
                        if (speedBoost) tempIt--;
                        tempViruses = ((HighBandwidthTile)adj).finalizeExpansion(tempIt, !speedBoost, startVirus);
                    }
                    else tempViruses = adj.finalizeExpansion(iters - 1, startVirus);

                    if (tempViruses != null) newViruses.AddRange(tempViruses);
                }
            }

            return newViruses;
        }
    }

    [Serializable]
    public class LowBandwidthTile : Tile
    {
        private HashSet<Tile> adjacentTries;

        public LowBandwidthTile()
            : base() { this.type = (int)TILE_VARIANTS.LOW_BANDWIDTH; }

        public LowBandwidthTile(Point myLocation)
            : base(myLocation) 
        {
            this.type = (int)TILE_VARIANTS.LOW_BANDWIDTH;
            this.adjacents = new List<Tile>();
            this.adjacentTries = new HashSet<Tile>();
        }
        
        //plant a virus on a tile
        public override void plantVirus(Virus v)
        {
            virus = v;
            this.adjacentTries.Clear();
        }

        public override void tryExpand(int iters, Virus startVirus)
        {

            if (iters == 0) return;

            List<Tile> expandAdjacents;
            if (startVirus.hasOutbreak()) expandAdjacents = getOutbreakAdjacents();
            else expandAdjacents = adjacents;

            foreach (Tile adj in expandAdjacents)
            {
                if (!markOtherTile(adj, startVirus, iters)) continue;

                bool isFriend = false;
                bool active = false;

                if (adj.isOccupied())
                {
                    isFriend = Virus.isFriend(startVirus, adj.getVirus());
                    active = adj.getVirus().isActive();
                }

                //Crawl through unoccupied tiles or inactive friendly ones
                if (!adj.isOccupied() || (isFriend && !active))
                {
                    //If this is the ending square of a path
                    if (iters == 1) {
                        if (adjacentTries.Contains(adj))
                        {
                                adjacentTries.Remove(adj);
                                adj.tryExpand(iters - 1, startVirus);
                        }
                        else {
                            //cannot access adjacent this turn
                            adj.removeMark(startVirus);
                            adjacentTries.Add(adj);
                        }
                    }
                    else
                    {
                        adj.tryExpand(iters - 2, startVirus);
                    }
                }
                else if (isFriend && active)
                {
                    //you can't try to move into an active friend!
                    adjacentTries.Remove(adj);
                }
            }
        }
    }

    [Serializable]
    public class WiFiTile : Tile
    {
        public WiFiTile()
            : base() { this.type = (int)TILE_VARIANTS.WIFI; }

        public WiFiTile(Point myLocation)
            : base(myLocation) { this.type = (int)TILE_VARIANTS.WIFI;}
    }

    [Serializable]
    public class BrokenLinkTile : Tile
    {
        public BrokenLinkTile()
            : base()
        {
            this.type = (int)TILE_VARIANTS.BROKEN_LINK;
        }

        public BrokenLinkTile(Point myLocation) 
            : base(myLocation)
        {
            this.type = (int)TILE_VARIANTS.BROKEN_LINK;
            this.location = myLocation;
            this.adjacents = new List<Tile>();
        }

        public override void placeAdjacents(List<Tile> adjacents) {}

        public override Virus placeBase(int playerIndex) {return null;}

        //plant a virus on a tile
        public override void plantVirus(Virus v) { }

    }

    [Serializable]
    public class BusTile : Tile
    {
        const int BUS_SPEED_BOOST = 2;
        List<Tile> inactiveAdjacents;
        //for this tile, up=down and left=right

        public BusTile()
            : base()
        {
            this.type = (int)TILE_VARIANTS.BUS;
            inactiveAdjacents = new List<Tile>();
            this.orientation = (int)DIRECTIONS.UP;
        }

        public BusTile(Point myLocation, int orientation) 
            : base(myLocation)
        {
            this.type = (int)TILE_VARIANTS.BUS;
            inactiveAdjacents = new List<Tile>();
            this.orientation = orientation;
        }

        public override void setOrientation(int direction)
        {
            base.setOrientation(direction);

            adjacents.AddRange(inactiveAdjacents);
            inactiveAdjacents.Clear();

            int modX1 = location.X;
            int modY1 = location.Y;
            int modX2 = modX1;
            int modY2 = modY1;
            switch (orientation)
            {
                case (int)DIRECTIONS.LEFT:
                case (int)DIRECTIONS.RIGHT: modY1++; modY2--; break;
                case (int)DIRECTIONS.UP:
                case (int)DIRECTIONS.DOWN: modX1++; modX2--; break;
            }
            Point closed1 = new Point(modX1, modY1);
            Point closed2 = new Point(modX2, modY2);

            int i = 0;
            while (i < adjacents.Count)
            {
                if (adjacents[i].getLocation() == closed1 ||
                    adjacents[i].getLocation() == closed2)
                {
                    inactiveAdjacents.Add(adjacents[i]);
                    adjacents.RemoveAt(i);
                }
                else i++;
            }
        }
        public int getOrientation() { return orientation; }

        public override void tryExpand(int iters, Virus startVirus)
        {
            tryExpand(iters, 0, startVirus);
        }

        public void tryExpand(int iters, int busBoost, Virus startVirus)
        {
            if (iters == 0) return;

            List<Tile> expandAdjacents;
            if (startVirus.hasOutbreak()) expandAdjacents = getOutbreakAdjacents();
            else expandAdjacents = adjacents;

            foreach (Tile adj in expandAdjacents)
            {
                //Mark expandable tile
                if (!markOtherTile(adj, startVirus, iters)) continue;

                //Crawl through unoccupied tiles or inactive friendly ones
                if (!adj.isOccupied() ||
                    ((Virus.isFriend(startVirus, adj.getVirus())) && !adj.getVirus().isActive()))
                {
                    if (adj is BusTile)
                    {
                        //if speedBoost = true, you're coming off a speed boost
                        int tempIt = iters;
                        int tempBoost = busBoost;
                        if (busBoost == BUS_SPEED_BOOST)
                        {
                            tempIt--;
                            tempBoost = 0;
                        }
                        else tempBoost++;

                        ((BusTile)adj).tryExpand(tempIt, tempBoost, startVirus);
                    }
                    else adj.tryExpand(iters - 1, startVirus);
                }
            }
        }

        public override List<Virus> finalizeExpansion(int iters, Virus startVirus)
        {
            return finalizeExpansion(iters, 0, startVirus);
        }

        public List<Virus> finalizeExpansion(int iters, int busBoost, Virus startVirus)
        {
            if (virus == null || iters == 0) return null;

            List<Tile> expandAdjacents = (startVirus.hasOutbreak() ? getOutbreakAdjacents() : adjacents);

            List<Virus> newViruses = new List<Virus>();
            List<Virus> tempViruses = new List<Virus>();

            //Investigate marks of every adjacent
            foreach (Tile adj in expandAdjacents)
            {
                if (!adj.canFinalize(startVirus, iters)) continue;

                bool addNewVirus = true;
                bool recurse = false;
                if (adj.isOccupied())
                {
                    bool isFriend = Virus.isFriend(virus.getPlayerIndex(), adj.getVirus());
                    if (!adj.getVirus().isActive())
                    {
                        addNewVirus = false;
                        if (!isFriend &&
                            (virus.getRawPotency() - (int)VIRUS_DEFAULTS.POTENCY_MOD ==
                             adj.getVirus().getRawPotency()))
                        {
                            adj.getVirus().kill();
                        }
                        else if (adj.getVirus().decayCopy(virus)) recurse = true;
                    }
                    else if(!isFriend) addNewVirus = !adj.defend(startVirus.getPlayerIndex());
                }
                if (addNewVirus)
                {
                    Virus newVirus = new Virus(this.virus, adj);
                    if (newVirus.isAlive())
                    {
                        newViruses.Add(newVirus);
                        recurse = true;
                    }
                    else newVirus.kill();
                }
                if (recurse)
                {
                    if (adj is BusTile)
                    {
                        int tempIt = iters;
                        int tempBoost = busBoost;
                        if (busBoost == BUS_SPEED_BOOST)
                        {
                            tempIt--;
                            tempBoost = 0;
                        }
                        else tempBoost++;
                        tempViruses = ((BusTile)adj).finalizeExpansion(tempIt, tempBoost, startVirus);
                    }
                    else tempViruses = adj.finalizeExpansion(iters - 1, startVirus);

                    if (tempViruses != null) newViruses.AddRange(tempViruses);
                }
            }

            return newViruses;
        }
    }

    [Serializable]
    public class DyingLinkTile : Tile
    {
        const int MAX_AGE = 10;
        int currentAge;

        public DyingLinkTile()
            : base()
        {
            this.type = (int)TILE_VARIANTS.DYING_LINK;
            currentAge = 0;
        }

        public DyingLinkTile(Point myLocation)
            : base(myLocation)
        {
            this.type = (int)TILE_VARIANTS.DYING_LINK;
            currentAge = 0;
        }

        public override bool age()
        {
            if (currentAge < MAX_AGE)
            {
                currentAge++;
            }
            else 
            {
                foreach (Tile t in adjacents)
                {
                    t.getAdjacents().Remove(this);
                }
                adjacents.Clear();
            }
            return (currentAge < MAX_AGE);
        }

        public virtual bool isAlive() { return (currentAge < MAX_AGE); }
    }

    [Serializable]
    public class FirewallTile : Tile
    {
        public int orientation;
        Tile inactiveAdjacent;

        public FirewallTile()
            : base()
        {
            this.type = (int)TILE_VARIANTS.FIREWALL;
            this.orientation = (int)DIRECTIONS.LEFT;
        }

        public FirewallTile(Point myLocation, int inactiveDirection) 
            : base(myLocation)
        {
            this.type = (int)TILE_VARIANTS.FIREWALL;
            this.orientation = inactiveDirection;
        }

        public override void setOrientation(int direction)
        {
            base.setOrientation(direction);
            if (inactiveAdjacent != null)
            {
                adjacents.Add(inactiveAdjacent);
                inactiveAdjacent = null;
            }

            int modX = location.X;
            int modY = location.Y;
            switch (orientation)
            {
                case (int)DIRECTIONS.LEFT: modX--; break;
                case (int)DIRECTIONS.UP: modY++; break;
                case (int)DIRECTIONS.DOWN: modY--; break;
                case (int)DIRECTIONS.RIGHT: modX++; break;
            }
            Point directionMod = new Point(modX, modY);

            int i = 0;
            while(i < adjacents.Count)
            {
                if (adjacents[i].getLocation() == directionMod)
                {
                    inactiveAdjacent = adjacents[i];
                    adjacents.RemoveAt(i);
                    break;
                }
                else i++;
            }
        }
        public int getInactiveDirection() { return orientation; }
    }

    #endregion
}